package com.easepal.softaplib;

import com.easepal.softaplib.log.LogUtil;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * 作者: bright.lin@medisana.net.cn
 * 时间: 2018/12/19
 * 标题: 数据读写处理
 * 描述: 处理实际Socket连接及数据读写
 * 修改: 由 bright.lin@medisana.net.cn 修改于 2018/12/19
 */
class BioReadWriteProcessor {
    private final static String TAG = BioReadWriteProcessor.class.getName();

    // ip地址
    private String mIp;
    // 端口
    private int mPort;
    // 连接超时时长
    private long connectTimeout;

    // Socket客户端
    private BioClient mClient;
    // 内部连接监听回调
    private BioConnectListener mConnectStatusListener;

    // Socket对象
    private Socket mSocket = null;
    // 输出流
    private OutputStream mOutputStream = null;
    // 输入流
    private InputStream mInputStream = null;

    // 写数据处理
    private WriteRunnable mWriteProcessor;

    // 写线程
    private Thread mWriteThread = null;
    // 读线程
    private Thread mReadThread = null;

    // 读写进程计数
    private int rwCount = 2;

    /**
     * 带参构造器
     * @param mIp ip地址
     * @param mPort 端口
     * @param connectTimeout 超时时长
     * @param mClient 客户端
     * @param mConnectionStatusListener 连接监听回调
     */
    BioReadWriteProcessor(String mIp, int mPort, long connectTimeout, BioClient mClient, BioConnectListener mConnectionStatusListener) {
        this.mIp = mIp;
        this.mPort = mPort;
        this.connectTimeout = connectTimeout;
        this.mClient = mClient;
        this.mConnectStatusListener = mConnectionStatusListener;
    }

    /**
     * 开始
     */
    public void start() {
        ConnectRunnable mConnectProcessor = new ConnectRunnable();
        Thread mConnectThread = new Thread(mConnectProcessor);
        mConnectThread.start();
    }

    /**
     * 关闭
     */
    synchronized void close() {

        wakeUp();

        try {
            if (null != mOutputStream) {
                mOutputStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mOutputStream = null;
        }

        try {
            if (null != mInputStream) {
                mInputStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mInputStream = null;
        }

        try {
            if (null != mSocket) {
                mSocket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mSocket = null;
        }

        try {
            if (null != mWriteThread && mWriteThread.isAlive()) {
                mWriteThread.interrupt();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mWriteThread = null;
        }

        try {
            if (null != mReadThread && mReadThread.isAlive()) {
                mReadThread.interrupt();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mReadThread = null;
        }
    }

    /**
     * 唤醒写处理
     */
    void wakeUp() {
        if (null != mWriteProcessor) {
            mWriteProcessor.wakeup();
        }
    }

    /**
     * 判断是否关闭Socket
     * @param exitCode 退出编码
     */
    private synchronized void onSocketExit(int exitCode) {
        --rwCount;
        boolean isWriterReaderExit = (rwCount <= 0);
        LogUtil.d(TAG, "onSocketExit exit_code " + exitCode + (exitCode == 1 ? " onWrite" : " onRead") + " isWriterReaderExit " + isWriterReaderExit);
        close();
        if (isWriterReaderExit) {
            if (null != mConnectStatusListener) {
                mConnectStatusListener.onConnectFailed(BioReadWriteProcessor.this);
            }
        }
    }

    /**
     * 连接线程
     */
    private class ConnectRunnable implements Runnable {

        @Override
        public void run() {
            boolean connectRet = false;
            try {
                mSocket = new Socket();
                mSocket.connect(new InetSocketAddress(mIp, mPort), (int) connectTimeout);

                mOutputStream = mSocket.getOutputStream();
                mInputStream = mSocket.getInputStream();

                mWriteProcessor = new WriteRunnable();
                ReadRunnable mReadProcessor = new ReadRunnable();

                mWriteThread = new Thread(mWriteProcessor);
                mReadThread = new Thread(mReadProcessor);

                mWriteThread.start();
                mReadThread.start();

                if (null != mConnectStatusListener) {
                    mConnectStatusListener.onConnectSuccess(BioReadWriteProcessor.this, mOutputStream, mInputStream);
                }
                connectRet = true;
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (!connectRet) {
                if (null != mConnectStatusListener) {
                    mConnectStatusListener.onConnectFailed(BioReadWriteProcessor.this);
                }
            }
        }
    }

    /**
     * 读线程
     */
    private class WriteRunnable implements Runnable {

        private final Object lock = new Object();

        void wakeup() {
            synchronized (lock) {
                lock.notifyAll();
            }
        }

        public void run() {
            try {
                while (mClient.onWrite()) {
                    synchronized (lock) {
                        lock.wait();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            onSocketExit(1);
        }
    }

    private class ReadRunnable implements Runnable {
        public void run() {
            mClient.onRead();
            onSocketExit(2);
        }
    }
}